package edu.chl.hajo.shop.utils;

import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.Persistence;
import javax.persistence.RollbackException;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;



/**
 * A container for entities, base class for OrderBook, ProductCatalogue,
 * CustomerRegistry The fundamental common operations are here (CRUD).
 *
 * T is type for items in container K is type of id (primary key)
 *
 * @author hajo
 */
public abstract class AbstractDAO<T, K>
        implements IDAO<T, K> {

private  EntityManagerFactory emf;


private final Class<T> clazz;
protected AbstractDAO(Class<T> clazz, String puName)
{
this.clazz = clazz;
emf = Persistence.createEntityManagerFactory(puName);
    }



    protected EntityManager getEM() {
        return emf.createEntityManager();
    }

    @Override
    public void add(T t) {
        EntityManager em = getEM();
        EntityTransaction tx = em.getTransaction();
        try {
            tx.begin();
            em.persist(t);
            tx.commit();
            
        } catch (RollbackException e) {
            if (tx.isActive()) {
                tx.rollback();
            }
            
            Logger.getLogger(AbstractDAO.class.getName()).log(Level.SEVERE, "Exception during Add operation", e);
           throw e;
        } finally {
            if (em.isOpen()) {
                em.close();
            }
        }
    }

   @Override
    public void remove(K id) {
        EntityManager em = emf.createEntityManager();
        EntityTransaction tx = em.getTransaction();
        try {
            T obj = em.getReference(clazz, id);
            tx.begin();
            em.remove(obj);
            tx.commit();
            em.close();
        } catch (RollbackException e) {
            if (tx.isActive()) {
                tx.rollback();
            }
            Logger.getLogger(AbstractDAO.class.getName()).log(Level.SEVERE, "Exception during remove operation", e);
          // throw e;
        } finally {
            if (em.isOpen()) {
                em.close();
            }
        }
    }

    @Override
    public T update(T t) {
        T newt = t;
        EntityManager em = emf.createEntityManager();
        EntityTransaction tx = em.getTransaction();
        try {
            
            tx.begin();
            newt = em.merge(t);
            tx.commit();
        } catch (RollbackException e) {
            if (tx.isActive()) {
                tx.rollback();
            }
            Logger.getLogger(AbstractDAO.class.getName()).log(Level.SEVERE, "Exception during Update operation", e);
           // throw e;
        } finally {
            if (null != em && em.isOpen()) {
                em.close();
            }
        }
        return newt;
    }

    @Override
    public T find(K id) {
        EntityManager em = emf.createEntityManager();
        T t = em.find(clazz, id);
        em.close();
        return t;
    }

    @Override
    public List<T> getAll() {
        EntityManager em = emf.createEntityManager();
        
        CriteriaBuilder cb  = em.getCriteriaBuilder();
        CriteriaQuery<T> cq = cb.createQuery(clazz);
        cq.select(cq.from(clazz));
               
        List<T> result = em.createQuery(cq).getResultList();
        em.close();
        return result;
    }

    @Override
    public int getCount() {
        EntityManager em = emf.createEntityManager();
    
        CriteriaBuilder cb  = em.getCriteriaBuilder();
        CriteriaQuery<Long> cq = cb.createQuery(Long.class);
        cq.select(cb.count(cq.from(clazz)));

        Long count = em.createQuery(cq).getSingleResult();
        em.close();
        return count.intValue();
    }

 
    public EntityManagerFactory getEmf() {
        return emf;
    }

   
}